#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"

#include "Drivers/spiffs_drv.h"

#define TAG                 "SPIFFS"

#define MAX_LOCAL_NUM       (200)
#define FILE_NAME           "/spiffs/data.txt"
static long count_line(void);

/**
 * @brief 初始化本地菜单文件
 *
 * 该函数用于初始化本地菜单存储文件。如果文件不存在或行数超过最大限制，
 * 则创建一个新的空文件。具体功能如下：
 * - 检查菜单文件是否存在
 * - 统计文件行数
 * - 当文件不存在或行数超过限制时创建新文件
 * - 正确关闭文件句柄
 *
 * @note 文件操作使用标准 C 文件 API
 */
static void local_menu_init(void)
{
    FILE *file = fopen(FILE_NAME, "r");
    ESP_LOGI(TAG,"Initialize the local menu...");
	long line_count = count_line();
	if(!file){
        
        ESP_LOGW(TAG,"New file");
		FILE *t = fopen(FILE_NAME, "w");
		fclose(t);
        return;
	}
    if(line_count >= MAX_LOCAL_NUM){
                
        ESP_LOGW(TAG,"New file");
		FILE *t = fopen(FILE_NAME, "w");
		fclose(t);
    }
    fclose(file);
}

static long count_line(void) 
{
	
	FILE *file = fopen(FILE_NAME, "r");
	if (file == NULL) {
		return -1;
	}
	long lineCount = 0;
	char buf[MENUMAX_LINE];

	while(fgets(buf,MENUMAX_LINE,file) != NULL){

		lineCount++;
	}
	
	fclose(file);
    ESP_LOGI(TAG,"The current number of menus:%ld",lineCount);
	return lineCount;
}



/**
 * @brief 初始化 SPIFFS 文件系统
 *
 * 该函数完成以下操作:
 * 1. 注册并挂载 SPIFFS 文件系统
 * 2. 执行文件系统一致性检查
 * 3. 获取并验证分区大小信息
 * 4. 如果发现不一致,执行修复操作
 * 5. 初始化本地菜单
 *
 * @note 如果挂载失败会尝试格式化文件系统
 * @note 分区标签为 NULL,表示使用默认分区
 * @note 最大允许同时打开文件数为 5
 */
void SpiffsInit(void){
    esp_vfs_spiffs_conf_t conf;
    ESP_LOGI(TAG, "Initializing SPIFFS");

    conf.base_path = "/spiffs",
    conf.partition_label = NULL,
    conf.max_files = 5,
    conf.format_if_mount_failed = true;

    esp_err_t ret = esp_vfs_spiffs_register(&conf);

    if (ret != ESP_OK)
    {
        if (ret == ESP_FAIL)
        {
            ESP_LOGE(TAG, "Failed to mount or format filesystem");
        }
        else if (ret == ESP_ERR_NOT_FOUND)
        {
            ESP_LOGE(TAG, "Failed to find SPIFFS partition");
        }
        else
        {
            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
        }
        return;
    }

    ESP_LOGI(TAG, "Performing SPIFFS_check().");
    ret = esp_spiffs_check(conf.partition_label);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret));
        return;
    }
    else
    {
        ESP_LOGI(TAG, "SPIFFS_check() successful");
    }

    size_t total = 0, used = 0;
    ret = esp_spiffs_info(conf.partition_label, &total, &used); // 查看文件系统空间
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s). Formatting...", esp_err_to_name(ret));
        esp_spiffs_format(conf.partition_label);
        return;
    }
    else
    {
        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
    }

    // Check consistency of reported partiton size info.
    if (used > total)
    {
        ESP_LOGW(TAG, "Number of used bytes cannot be larger than total. Performing SPIFFS_check().");
        ret = esp_spiffs_check(conf.partition_label);
        // Could be also used to mend broken files, to clean unreferenced pages, etc.
        // More info at https://github.com/pellepl/spiffs/wiki/FAQ#powerlosses-contd-when-should-i-run-spiffs_check
        if (ret != ESP_OK)
        {
            ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret));
            return;
        }
        else
        {
            ESP_LOGI(TAG, "SPIFFS_check() successful");
        }
    }

    local_menu_init();
}

/**
 * @brief 设置本地菜谱数据
 *
 * 在本地文件中设置或更新指定键值对的菜谱数据。如果键已存在则更新对应的值，
 * 如果键不存在则添加新的键值对。使用临时文件进行更新操作以确保数据完整性。
 *
 * @param key 菜谱的id
 * @param value 菜谱的值
 *
 * @note 文件操作失败时会打印错误日志
 * @note 成功添加新菜谱时会打印成功日志
 */
void setLocalRecipe(const char *key, const char *value)
{
    FILE *file = fopen(FILE_NAME, "r");
    FILE *temp_file = fopen("/spiffs/temp.txt", "w");

	if(!file){

        ESP_LOGE(TAG,"File error");	
        return;
	}

	bool found = false;
	char line[MENUMAX_LINE];

	while (fgets(line, sizeof(line), file)) {

		char current_key[IDMAX_LINE];

		sscanf(line, "%[^=]", current_key);     //=前是key
		if (strcmp(current_key, key) == 0) {

			fprintf(temp_file, "%s=%s\n", key, value); // 替换值
			found = true;
		} else {

			fputs(line, temp_file); // 保留原始行
		}

	}
	if(!found){
        
        
		fprintf(temp_file, "%s=%s\n", key, value); // 添加新键值对
        ESP_LOGI(TAG,"save recipe success!!!");
	}

	fclose(file);
	fclose(temp_file);
	remove(FILE_NAME);
	rename("/spiffs/temp.txt", FILE_NAME);
}

/**
 * @brief 从本地菜单文件中读取指定键值对应的内容
 *
 * @param key 要查找的键名
 * @param buf 用于存储找到的值的缓冲区
 * @return bool 如果找到键值对返回 true，否则返回 false
 *
 * @details 该函数打开本地菜单文件，按行读取并解析键值对。
 *          当找到匹配的键时，将对应的值复制到提供的缓冲区中。
 *          如果文件不存在或无法打开，将清空缓冲区并返回 false。
 */
bool get_local_menu(const char *key, char *buf)
{
    FILE *file = fopen(FILE_NAME, "r");
    char line[MENUMAX_LINE];
	char current_key[IDMAX_LINE];
    bool found = false;
	// char *value = (char*)malloc(MENUMAX_LINE);          //free
	if (!file) {

		ESP_LOGE(TAG,"File error");	
        memset(buf,0,MAX_LOCAL_NUM);
		return false;
	}
	
	while (fgets(line, sizeof(line), file)) {
		sscanf(line, "%[^=]=%[^\n]", current_key, line);
		// ESP_LOGI(TAG,"look_key:%s",key);
        // ESP_LOGI(TAG,"current_key:%s",current_key);
        
		if (strncmp(current_key, key,10) == 0) {

            strcpy(buf,line);
            found = true;
			break;
		}
	}
	
	fclose(file);
	return found;
}